/* --------------------------------------------------------
 * Copyright (c) Express Scripts, Inc.  All rights reserved.
 * --------------------------------------------------------
 */
package com.aeky.server.dao.impl;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.Session;
import org.springframework.util.Assert;

import com.aeky.common.ReflectionUtils;

/**
 * The Simple Class for DaoSupport, do not support paginate search.
 * 
 * @param <T> domain object class
 */
public class SimpleDaoSupport<T> {

    /** The entity manager. */
    private EntityManager entityManager;

    /** The entity class. */
    private Class<T> entityClass;

    /**
     * Gets the entity class.
     * 
     * @return the entity class
     */
    public Class<T> getEntityClass() {
        return entityClass;
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#setEntityClass(java.lang.Class)
     */
    public void setEntityClass(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    /**
     * Instantiates a new simple dao support.
     */
    public SimpleDaoSupport() {
        this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
    }

    /**
     * Sets the entity manager.
     * 
     * @param entityManager the new entity manager
     */
    @PersistenceContext(unitName = "demo")
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    /**
     * Gets the entity manager.
     * 
     * @return the entity manager
     */
    protected EntityManager getEntityManager() {
        return entityManager;
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#add(java.lang.Object)
     */
    public void add(Object model) {
        this.entityManager.persist(model);
    }

    /***
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#update(java.lang.Object)
     */
    public T update(T entity) {
        return entityManager.merge(entity);

    }

    /**
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#delete(java.lang.Object)
     */
    public void delete(Object model) {
        this.entityManager.remove(model);
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#load(java.lang.Object)
     */
    public T load(Object primaryKey) {
        return entityManager.find(entityClass, primaryKey);
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#getAll()
     */
    @SuppressWarnings("unchecked")
    public List<T> getAll() {
        return this.entityManager.createQuery("select obj from " + entityClass.getName() + " obj").getResultList();
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.esi.bfp.server.dao.BaseDao#getAll(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public List<T> getAll(String orderBy) {
        return this.entityManager.createQuery("select obj from " + entityClass.getName() + " obj " + orderBy)
                .getResultList();
    }

    /**
     * Creates the query.
     * 
     * @param queryString the query string
     * @param params the params
     * @param isNative the is native
     * 
     * @return the query
     */
    protected Query createQuery(final String queryString, Map<String, ?> params, boolean isNative) {
        Assert.hasText(queryString, "queryString can not empty");
        Query query = null;
        if (!isNative) {
            query = entityManager.createQuery(queryString);
        } else {
            query = entityManager.createNativeQuery(queryString);
        }
        if (params != null) {
            for (Map.Entry<String, ?> entry : params.entrySet()) {
                if (entry.getValue() != null && entry.getValue().toString().trim().length() > 0) {
                    query.setParameter(entry.getKey(), entry.getValue());
                }
            }
        }
        return query;
    }

    protected List<T> createNativeQuery(final String queryString, Object... params) {
        Assert.hasText(queryString, "queryString can not empty");
        Query query = entityManager.createNativeQuery(queryString, entityClass);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        return query.getResultList();
    }

    /**
     * Creates the query.
     * 
     * @param queryString the query string
     * @param params the params
     * 
     * @return the query
     */
    protected Query createQuery(final String queryString, Object... params) {
        Assert.hasText(queryString, "queryString can not empty");
        Query query = entityManager.createQuery(queryString);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        return query;
    }

    /**
     * Find.
     * 
     * @param queryString the query string
     * @param params the params
     * 
     * @return the list< t>
     */
    @SuppressWarnings("unchecked")
    protected List<T> find(final String queryString, Object... params) {
        Query query = entityManager.createQuery(queryString);
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        return query.getResultList();
    }

    /**
     * Gets the hibernate session.
     * 
     * @return the hibernate session
     */
    protected Session getHibernateSession() {
        return (Session) entityManager.getDelegate();
    }

    /**
     * Find unique.
     * 
     * @param hql the hql
     * @param values the values
     * @param <X> generic Class
     * 
     * @return the x
     */
    @SuppressWarnings("unchecked")
    protected <X> X findUnique(String hql, Map<String, Object> values) {
        return (X) createQuery(hql, values, false).getSingleResult();
    }

    /**
     * Find unique.
     * 
     * @param hql the hql
     * @param values the values
     * @param <X> generic Class
     * 
     * @return the x
     */
    @SuppressWarnings("unchecked")
    protected <X> X findUnique(String hql, Map<String, Object> values, boolean isNative) {
        return (X) createQuery(hql, values, isNative).getSingleResult();
    }

    /**
     * Find unique.
     * 
     * @param hql the hql
     * @param values the values
     * @param <X> generic Class
     * 
     * @return the x
     */
    @SuppressWarnings("unchecked")
    public <X> X findUnique(final String hql, final Object... values) {
        return (X) createQuery(hql, values).getSingleResult();
    }

}
